# To change this template, choose Tools | Templates
# and open the template in the editor.

#!/usr/bin/env python
#coding: iso-8859-1

"""Documentation for tunnel.py

This file contains the class tunnel for handling world maps

project -- Rez: The Clone
author -- Edwood Grant
organization -- Binhex Tw.
version -- 1.00
since -- 7/07/2007 23:04:41
"""

#Panda specific imports for this module
import direct.directbase.DirectStart
from pandac.PandaModules import *
from pandac.PandaModules import ShaderGenerator
from panda3d.core import loadPrcFileData
from panda3d.physics import BaseParticleEmitter,BaseParticleRenderer
from panda3d.physics import PointParticleFactory,SpriteParticleRenderer
from panda3d.physics import LinearNoiseForce,DiscEmitter

from direct.particles.Particles import Particles
from direct.particles.ForceGroup import ForceGroup
from direct.particles.ParticleEffect import ParticleEffect
# Configure the parallax mapping settings (these are just the defaults)
loadPrcFileData("", "parallax-mapping-samples 3")
loadPrcFileData("", "parallax-mapping-scale 0.1")

#Librerias propias
import globals
import midifile

class Tunnel:
    def __init__(self):
        """Constructor

        We are not doing anything special here, only when we decide to init
        right here we are only creating some variables
        """
        self.models = []
        self.bgMusic = []
        self.name = []
        self.changer = []
        self.changingUp = None
        self.changingDown = None
        self.actStage = 0
        self.transp = 0
        self.moved = 0
        self.loop = None
        self.backMusic = None
        self.delay = 0
        self.midinotes = []
        self.midichannels = []

    def init(self, lvl):
        """Init Function

        Initializes and runs everything related to tunnel and maps
        """
        #If the parameter lvl if bigger than
        #the amount of levels, we wont do anything
        if lvl >= len(self.models):
            return
        #Loads sound, according to lvl parameter
        #and if there any sounds in the correspondant level
        if not (self.bgMusic[lvl] == "" or self.bgMusic[lvl] == None):
            #Try to stop the previous music, if we are in the middle of a
            #level this must exist already, or it is None if its the beggining
            if self.backMusic != None:
                self.backMusic.stop()
            #Load music, set it to lopp if requested, and play
            self.backMusic = base.loadMusic(self.bgMusic[lvl])
            self.backMusic.setVolume(.8)
            self.backMusic.setLoop(False)
            self.backMusic.setPlayRate(globals.RATE)
           

         

        #Define and load everything
        if lvl == 0: #The node hasnt been created
            self.node = render.attachNewNode(self.name[lvl])
            #Enable transparency
            #self.node.setAlphaScale(self.transp)

        #We try to remove these nodes. If we are in the middle of a level
        #These node must exist already
        if lvl != 0:
            self.model.removeNode()
            self.model2.removeNode()
            self.model3.removeNode()
            self.model5.removeNode()
            self.model4.removeNode()
        #We are going to move this and look like the infinite tunnel demo from panda 3D
        #So we are loading two other instances of this model
        self.model = loader.loadModelCopy(self.models[lvl])
        self.model2 = loader.loadModelCopy(self.models[lvl])
        self.model3 = loader.loadModelCopy(self.models[lvl])
        self.model4 = loader.loadModelCopy(self.models[lvl])
        self.model5 = loader.loadModelCopy(self.models[lvl])
        #We will enable transaparency on these model as well
#        self.model.setTransparency(1)
#        self.model2.setTransparency(1)
#        self.model3.setTransparency(1)
#        self.node.setTransparency(1)
        #self.node.setDepthOffset(-1)

        #This changing size and scaling and rotating, are just
        #a horrible blatant hack, im puttin them just to make
        #the models fit well in the screen
        self.model.setScale(1, 1, 1)
        self.model.setR(90)
        self.model2.setScale(1, 1, 1)
        self.model2.setR(90)
        self.model3.setScale(1, 1, 1)
        self.model3.setR(90)

        self.model4.setScale(1, 1, 1)
        self.model4.setR(90)

        self.model5.setScale(1, 1, 1)
        self.model5.setR(90)
        #We set it accordin to a nice position for our player, another hack again
#        self.node.setZ(222)
#        self.node.setY(222)
#        self.node.setX(1225)
        self.model4.setPos(3000, 0, 0)
        self.model5.setPos(4000, 0, 0)
        self.model2.setPos(1000, 0, 0)

        self.model3.setPos(2000, 0, 0)
        self.model.setPos(0,0,0)

        #Make it visible
        self.model.reparentTo(self.node)
        self.model2.reparentTo(self.node)
        self.model3.reparentTo(self.node)
        self.model4.reparentTo(self.node)
        self.model5.reparentTo(self.node)
        #And we put it in the place where the previous tunnel was
        #on relation to itself, if this is the fires self.moved = 0
        self.node.setPos(self.node, self.moved, 0, 0)



#        self.fog = Fog('distanceFog')
#        self.fog.setColor(0, 0, 0)
#        self.fog.setExpDensity(.00003)
#        render.setFog(self.fog)


        self.p = ParticleEffect()
        self.p.reset()
        
        self.p.setPos(1500.000, 0.000, 0.000)
        self.p.setHpr(0.000, 0.000, 0.000)
        self.p.setScale(10.000, 10.000, 10.000)
        p0 = Particles('particles-1')
        # Particles parameters
        p0.nodePath.setShaderOff()
        # Particles parameters
        p0.setFactory("PointParticleFactory")
        p0.setRenderer("LineParticleRenderer")
        p0.setEmitter("SphereSurfaceEmitter")
        p0.setPoolSize(64)
        p0.setBirthRate(0.0815000)
        p0.setLitterSize(10)
        p0.setLitterSpread(0)
        p0.setSystemLifespan(0.0000)
        p0.setLocalVelocityFlag(1)
        p0.setSystemGrowsOlderFlag(0)
        # Factory parameters
        p0.factory.setLifespanBase(3.5000)
        p0.factory.setLifespanSpread(1.0000)
        p0.factory.setMassBase(1.0000)
        p0.factory.setMassSpread(0.0000)
        p0.factory.setTerminalVelocityBase(800.0000)
        p0.factory.setTerminalVelocitySpread(0.0000)
        # Point factory parameters
        # Renderer parameters
        p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAINOUT)
        p0.renderer.setUserAlpha(0.70)
        # Line parameters
        p0.renderer.setHeadColor(Vec4(1.00, 1.00, 0.50, 1.00))
        p0.renderer.setTailColor(Vec4(.20, .20, .10, 1.00))
        p0.renderer.setLineScaleFactor(2.00)
        # Emitter parameters
        p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE)
        p0.emitter.setAmplitude(1.0000)
        p0.emitter.setAmplitudeSpread(0.0000)
        p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000))
        p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000))
        p0.emitter.setRadiateOrigin(Point3(1000.0000, 0.0000, 0.0000))
        # Sphere Surface parameters
        p0.emitter.setRadius(25.00)


        self.p.addParticles(p0)
        f0 = ForceGroup('gravity')
        # Force parameters
        force0 = LinearVectorForce(Vec3(-30.0000, 0.0000, 0.0000), 1.0000, 0)
        force0.setVectorMasks(1, 1, 1)
        force0.setActive(1)
        f0.addForce(force0)
        self.p.addForceGroup(f0)
        self.p.start(render)
        #Special configuration if this is the first level loaded


        self.ambientLight = AmbientLight("ambientLight")
        self.ambientLight.setColor(Vec4(0.086, 0.088, 0.080, 1))
        ambientLightNP = render.attachNewNode(self.ambientLight)
        render.setLight(ambientLightNP)

	self.dlight = DirectionalLight('dlight')
        self.dlight.setColor(VBase4(0.1, 0.2, 0.2, 1))
        self.dlight.setSpecularColor(VBase4(0.2, 0.3, 0.3, 1))
        self.dlnp = render.attachNewNode(self.dlight)
        self.dlnp.setHpr(0, 160,0)
        render.setLight(self.dlnp)



        if lvl == 0:
            #Turn of all lights
            #ambient light


            # Add a light to the scene.
            #self.lightpivot = render.attachNewNode("lightpivot")
            #self.lightpivot.hprInterval(10,Point3(180,0,0)).loop()
#            plight = PointLight('plight')
#            plight.setColor(Vec4(1, 1, 1, 1))
#            #plight.setAttenuation(Vec3(0.7,0.05,0))
#            self.plnp = render.attachNewNode(plight)
#            self.plnp.setPos(0, 0, 0)
#            self.node.setLight(self.plnp)
            #self.node.setShaderInput("light", self.plnp)




            #Update Task
            self.updateTask = taskMgr.add(self._update, 'Update-Tunnel')
            self.updateTask.last = 0
            #This task is to make the map movement return to where it was before,
            #so the illusion of a seamlees world is mantained
            taskMgr.doMethodLater(10, self._returnToStart, 'Return-Tunnel')

        #Our model if fading from black now, so its 'changingUp'
        self.changingUp = True

    def _returnToStart(self, task):
        """Task Function

        It will return to the beginning the whole tunnel, and will reset this task
        """
        self.node.setX(self.node, -self.moved)
        self.moved = 0
        taskMgr.doMethodLater(10, self._returnToStart, 'Return-Tunnel')
        return task.done

    def _update(self, task):
        """Task function

        This will update the state of the tunnel
        """
        #Dt handling to make movement independent of framerate
        dt = task.time - task.last
        task.last = task.time

        #print self.node.getState()
#        if self.loop and self.backMusic.status() == self.backMusic.READY:
#            print "looped_music"
#            midifile.createEnemies(self.midinotes, self.midichannels)
#            self.backMusic.play()
#
        #self.lightpivot.setPos(globals.PlayerPosition)
#        self.plnp.setPos(globals.PlayerPosition)
#        print globals.PlayerPosition
#        self.plnp.lookAt(Point3(globals.PlayerPosition.getX()+100,globals.PlayerPosition.getY(),globals.PlayerPosition.getZ()-10))


        #Tunnel constanly keep monitoring enemy changers,
        #so if changer node is empty, it means it was destroyed,
        #So we change to a new level if we have more levels
        #if not self.actStage >= len(self.models) - 1:
        #    if self.changer[self.actStage].node.isEmpty():
        #        self._change(self.actStage + 1)
        if globals.STAGENUM != self.actStage:
            self._change(self.actStage+1)

        #If we are 'changing down', we are doing a fade to black
        #We do that by changing its transparency
        #if self.changingDown == True:
            #If we are done then we call _initlvl
            #if self.transp < 0:
                #self.transp = 0
                #self._initLvl(self.actStage)
            #self.node.setAlphaScale(self.transp)
            #self.transp -= 0.01

        #If we are 'changing down', we are undoing the fade to black
        #We do that by changing its transparency
        #if self.changingUp == True:
            #if self.transp > 1:
                #self.transp = 1
                #self.changingUp = False
            #self.node.setAlphaScale(self.transp)
            #self.transp += 0.01

		#print self.node.getPos()
        #The world is always moving backwards to make look that the player is moving
        self.node.setPos(self.node, -100 * dt, 0, 0)
        self.moved -= 100 * dt
        #print self.node.getPos(),self
        return task.cont

    def startLevel(self):
        """Start Level function
        load a .mid file and start .wav music
        """
        midifile.createEnemies(self.midinotes, self.midichannels)

        self._change(0)
        if self.backMusic:    
            self.delay = globalClock.getFrameTime()
            self.backMusic.play()
        print "STARTED LEVEL MUSIC"
        
    def _change(self, lvl):
        """Change function

        This will issue a change to a specific level
        """
        self.actStage = lvl
        #If the level is different from 0, we only
        #have to make a changedown-up setting. (this assumes level 0 is loaded already)
        #but if it is 0, then we intialize everything normaly
        if lvl != 0:
            self.changingDown = True
            self.changingUp = False
            self.transp = 1
        else:
            self._initLvl(lvl)

    def _initLvl(self, lvl):
        """Init Level function

        This function will intialize enemies and maps to render
        """
        #We must disable changing down, we start from black
        #self.changingDown = False
        #We load all the enemies related to this stage
        #And we are good to go and init()
        for i in globals.ENEMIES:
            obj = globals.ENEMIES[i]
            if obj.stageBelong == lvl:
                obj.init()
                print "Initialized object: " + obj
        
